package com.zj.job;

import com.zj.bean.SsmConfig;
import com.zj.controller.SsmCtl;
import javafx.application.Platform;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.ProgressCallback;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.DefaultShellCallback;

public class SsmJob extends Thread{

    private SsmConfig config;
    private SsmCtl controller;

    public SsmJob(SsmConfig config,SsmCtl controller) {
        this.config=config;
        this.controller=controller;
    }



    @Override
    public void run(){
        List<String> tableList=new ArrayList();
        getTableList(tableList,config.getTableStr());
        if(tableList.size()==0){
//            sendChat("wrong param, example:\n code table1 table2 ...");
            return;
        }

        generate(tableList);

        log("任务执行成功");

    }

    public boolean generate(List<String> tableList){
        log("start generate MBG obj");
        List<String> warnings = new ArrayList<String>();
        try {
            Configuration configuration = new Configuration();
            configuration.addClasspathEntry(config.getConnectorPath());
            Context context=new Context(ModelType.CONDITIONAL);
            context.setId("defaultContext");


            CommentGeneratorConfiguration commentGeneratorConfiguration=new CommentGeneratorConfiguration();
            commentGeneratorConfiguration.addProperty("suppressAllComments","true");
            context.setCommentGeneratorConfiguration(commentGeneratorConfiguration);

            JDBCConnectionConfiguration jdbcConnectionConfiguration=new JDBCConnectionConfiguration();
            jdbcConnectionConfiguration.setDriverClass("com.mysql.jdbc.Driver");
            jdbcConnectionConfiguration.setConnectionURL(config.getJdbcUrl());
            jdbcConnectionConfiguration.setUserId(config.getUserName());
            jdbcConnectionConfiguration.setPassword(config.getPassword());
            context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);

            JavaModelGeneratorConfiguration javaModelGeneratorConfiguration=new JavaModelGeneratorConfiguration();
            javaModelGeneratorConfiguration.setTargetPackage(config.getBasePackage()+".model");
            javaModelGeneratorConfiguration.setTargetProject(config.getOrmJavaPath() + "/java/");
            context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);

            JavaClientGeneratorConfiguration javaClientGeneratorConfiguration=new JavaClientGeneratorConfiguration();
            javaClientGeneratorConfiguration.setTargetPackage(config.getBasePackage() + ".dao");
            javaClientGeneratorConfiguration.setTargetProject(config.getOrmJavaPath() + "/java/");
            javaClientGeneratorConfiguration.setConfigurationType("XMLMAPPER");
            context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);

            SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration=new SqlMapGeneratorConfiguration();
            sqlMapGeneratorConfiguration.setTargetPackage("orm");
            sqlMapGeneratorConfiguration.setTargetProject(config.getOrmJavaPath()+"/resources/");
            context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);




            for(String tableName:tableList){
                TableConfiguration tableConfiguration=new TableConfiguration(context);
                tableConfiguration.setSchema(config.getDatabase());
                tableConfiguration.setTableName(tableName);
                tableConfiguration.setDomainObjectName(convertDomainName(tableName));
                GeneratedKey generatedKey=new GeneratedKey("id","MySql",true,null);
                tableConfiguration.setGeneratedKey(generatedKey);
                context.addTableConfiguration(tableConfiguration);

                cleanFile(config.getOrmJavaPath()+"/resources/orm/"+tableConfiguration.getDomainObjectName()+"Mapper.xml");
            }

            configuration.addContext(context);
            boolean overwrite = true;
            DefaultShellCallback callback = new DefaultShellCallback(overwrite);
            MyBatisGenerator myBatisGenerator = new MyBatisGenerator(configuration, callback, warnings);



            myBatisGenerator.generate(new TemplateGenerate(configuration));

        }catch (Exception e){
            e.printStackTrace();
            log(e.getMessage());
            log("encounter error when generate code:\n"+e.getMessage());
            return false;
        }
        return true;
    }
    private void cleanFile(String path){
        log("cleanFile:" + path);
        File file=new File(path);
        try {
            if(file.exists()) {
                FileUtils.forceDelete(file);
            }
        } catch (IOException e) {
            e.printStackTrace();
            log("encounterred error when clean:"+path);
            log(e.getMessage());
        }
    }

    private String convertDomainName(String tableName){
        String className="";
        String[] array = tableName.split("_");
        for (int i = 0; i < array.length; i++) {
            className += array[i].substring(0, 1).toUpperCase() + array[i].substring(1);
        }
        return className;
    }

    private void getTableList(List<String> tableList,String command){
        String[] cmdArr=command.split(" ");
        for(String tableName:cmdArr){
                tableList.add(tableName.trim());
        }
    }


    private void log(final String msg) {
        if (StringUtils.isEmpty(msg)) {
            return;
        }
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                controller.log(msg);
            }
        });
    }


    class TemplateGenerate implements ProgressCallback {

        private Configuration configuration;
        private VelocityContext context;
        private VelocityEngine ve;
        private String ormPath;
        private String servicePath;
        private String controllerPath;
        private String controllerPackagePath;
        private String basePackageFolder;

        private Template mapperTemplate;
        private Template serviceTemplate;
        private Template implTemplate;
        private Template validatorTemplate;
        private Template controllerTemplate;
        private Template apiTemplate;


        public TemplateGenerate(Configuration configuration){
            log("init TemplateGenerate");
            this.configuration=configuration;
            context = new VelocityContext();
            String basePackage=config.getBasePackage();
            String controllerPackage=config.getControllerPackage();
            context.put("basePackage",basePackage);
            context.put("modelPackage",basePackage+".model");
            context.put("daoPackage", basePackage+".dao");
            context.put("servicePackage",basePackage+".service");
            context.put("validatorPackage",controllerPackage+".validator");
            context.put("controllerPackage",controllerPackage);
            context.put("apiPackage",basePackage+".api");
            if(config.getBasePackage()!=null){
                context.put("baseController",config.getBaseController());
            }
            if(config.getBaseMapper()!=null){
                context.put("baseMapper",config.getBaseMapper());
            }
            if(config.getBaseServiceImpl()!=null){
                context.put("baseServiceImpl",config.getBaseServiceImpl());
            }
            if(config.getBaseService()!=null){
                context.put("baseService",config.getBaseService());
            }

            ormPath=config.getOrmJavaPath();
            servicePath=config.getServiceJavaPath();
            controllerPath=config.getControllerJavaPath();
            basePackageFolder="/java/"+basePackage.replaceAll("\\.","/");

            controllerPackagePath=config.getControllerJavaPath();

            ve = new VelocityEngine();
            ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
            ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());

            mapperTemplate = ve.getTemplate("template-vm/mapperTemplate.vm");
            serviceTemplate = ve.getTemplate("template-vm/serviceTemplate.vm");
            implTemplate = ve.getTemplate("template-vm/serviceImplTemplate.vm");
            validatorTemplate = ve.getTemplate("template-vm/validatorTemplate.vm");
            controllerTemplate = ve.getTemplate("template-vm/controllerTemplate.vm");
//            apiTemplate = ve.getTemplate("template-vm/apiTemplate.vm");

        }

        @Override
        public void introspectionStarted(int totalTasks) {
            log("introspectionStarted:totalTasks:" + totalTasks);

        }

        @Override
        public void generationStarted(int totalTasks) {
            log("generationStarted:totalTasks:" + totalTasks);
//            checkFolder(ormPath, "resources", false);
//            checkFolder(outputRootPath, "src", false);
//            checkFolder(baseOutputPath, "service", false);
//            checkFolder(baseOutputPath,"service"+File.separator+"impl",false);
//            checkFolder(baseOutputPath,"validator",false);
//            checkFolder(baseOutputPath,"controller",false);
        }


        private void checkFolder(String parentPath,String folderName,boolean cleanFolder){
            try {
                File folder=new File(parentPath+File.separator+folderName);
                if(cleanFolder && folder.exists()){
                    FileUtils.deleteDirectory(folder);
                }
                if(!folder.exists()){
                    folder.mkdirs();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        @Override
        public void saveStarted(int totalTasks) {

        }

        @Override
        public void startTask(String taskName) {

        }

        @Override
        public void done() {
            log("done");
            log("MBG done, start velocity generator!");
            List<TableConfiguration> tableConfigurationList=this.configuration.getContext("defaultContext").getTableConfigurations();

            for(TableConfiguration tableConfiguration:tableConfigurationList){
                String modelClass=tableConfiguration.getDomainObjectName();
                String modelFilePath=ormPath+basePackageFolder+File.separator+"model"+File.separator+modelClass+".java";
                File modelFile=new File(modelFilePath);
                if(!modelFile.exists()){
                    log(modelFile.getName()+" not exists");
                    continue;
                }
                context.put("modelClass", modelClass);
                context.put("modelClassParam", lowFirstChar(modelClass));
                writeTemplate(ormPath+basePackageFolder+File.separator+"dao",modelClass,mapperTemplate,"Mapper");
                writeTemplate(servicePath+basePackageFolder+File.separator+"service",modelClass,serviceTemplate,"Service");
                writeTemplate(servicePath+basePackageFolder+File.separator+"service"+File.separator+"impl",modelClass,implTemplate,"ServiceImpl");
                if(!StringUtils.isEmpty(controllerPath)){
                    writeTemplate(controllerPath+File.separator+"validator",modelClass,validatorTemplate,"Validator");
                    writeTemplate(controllerPath+File.separator,modelClass,controllerTemplate,"Controller");
                }
//                writeTemplate(controllerPath+controllerPackagePath+File.separator,modelClass,apiTemplate,"Api");
                addDateFormatCode(modelFile,StringUtils.isNotEmpty(config.getModelPath()));
                if (StringUtils.isNotEmpty(config.getModelPath())) {
                    moveModelClass(modelFile,config.getModelPath());
                }
            }

        }

        private String lowFirstChar(String tempStr){
            char[] cs=tempStr.toCharArray();
            cs[0]+=32;
            return String.valueOf(cs);
        }

        private void writeTemplate(String folderName,String modelClass,Template template,String suffix){
            try {
                String filePath=folderName+File.separator+modelClass+suffix+".java";
                File file=new File(filePath);
                if(file.exists() && !"Mapper".equals(suffix)){
                    log("skip exists file:"+file.getName());
                    return;
                }
                FileWriter writer=new FileWriter(filePath);
                template.merge(context, writer);
                writer.flush();
                writer.close();
            }catch (Exception e){
                e.printStackTrace();
                log("encounter error when write file:"+folderName+File.separator+modelClass+suffix);
                log(e.getMessage());
            }

        }
        @Override
        public void checkCancel() throws InterruptedException {

        }

        public void addDateFormatCode(File modelFile,boolean addSerializable){
            try {
                List<String> codeList= FileUtils.readLines(modelFile,"UTF-8");
                List<Integer> addList=new ArrayList();
                Integer classLineIndex=0;
                String newCline=null;
                for(int i=0;i<codeList.size();i++){
                    String codeLine=codeList.get(i);
                    if(codeLine.contains("private Date")){
                        addList.add(i);
                    }
                    if (addSerializable && codeLine.contains("public class")) {
                        classLineIndex=i;
                        newCline=codeLine.replace("{","implements Serializable{");
                    }
                }
                int offset=1;
                if(addSerializable && classLineIndex>0 && newCline!=null){
                    codeList.add(classLineIndex,newCline);
                    codeList.remove(classLineIndex + 1);
                    codeList.add(2,"import java.io.Serializable;");
                    offset=2;
                }
                if(addList.size()>0){
                    codeList.add(2,"import com.fasterxml.jackson.annotation.JsonFormat;");


                    for(Integer index:addList){
                        codeList.add(index+offset,"    @JsonFormat(pattern=\"yyyy-MM-dd HH:mm:ss\", timezone=\"GMT+8\")");
                        offset++;
                    }
                    FileUtils.writeLines(modelFile,codeList);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void moveModelClass(File modelFile,String modelPath){
            try {
                File file=new File(modelPath+File.separator+modelFile.getName());
                if (file.exists()) {
                    FileUtils.forceDelete(file);
                }
                FileUtils.moveFileToDirectory(modelFile,new File(modelPath),false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
